import { instantiate, isValid } from "cc";
import BaseWidget from "./BaseWidget";
import Dispatcher from "./DispatchMgr";
import LifeCycle from "./LifeCycle";
import Log from "./LogMgr";
import Res from "./ResMgr";

/**
 * 时钟回调函数类型
 * @returns completed 是否完成
 */
type ClockFn = () => boolean;

interface IWidgetData {
    uuid: string;
    comp: BaseWidget | null;
}


export default class Foundation extends LifeCycle {

    public addWidget(bdlname: string, clsname: string, data?: any, suffix?: string) {
        const uuid = suffix ? `${clsname}_${suffix}` : clsname;
        for (let widgetData of this._widgetDatas) {
            if (widgetData.uuid == uuid) return;
        }
        this._widgetDatas.push({ uuid, comp: null });

        Res.manualLoadPrefab(bdlname, clsname)
            .then(prefab => {
                let parent = this.node;
                let node = instantiate(prefab),
                    comp = node.getComponent(clsname) as BaseWidget;

                comp.setData(data);
                comp.suffix = suffix ? suffix : '';
                parent.addChild(node);

                this.insertWidget(uuid, comp);

                Log.i(`create widget ${uuid} ok!`);
            })
            .catch((err: Error) => {
                Log.e(`create ${clsname} err: ` + err.message);
                this.removeWidget(uuid);
            });
    }

    public getWidget<T extends BaseWidget>(clsName: string, suffix?: string): T | null {
        const uuid = suffix ? `${clsName}_${suffix}` : clsName;
        for (let widgetData of this._widgetDatas) {
            if (widgetData.uuid == uuid && widgetData.comp && isValid(widgetData.comp)) return widgetData.comp as T;
        }
        return null;
    }

    public removeWidget(uuid: string) {
        for (let l = this._widgetDatas.length, i = l - 1; i >= 0; --i) {
            let widgetData = this._widgetDatas[i];
            if (widgetData.uuid == uuid) {
                this._widgetDatas.splice(i, 1);
                if (widgetData.comp && isValid(widgetData.comp)) {
                    widgetData.comp.node.destroy();
                }
                break;
            }
        }
    }

    public setVisible(visible: boolean) {
        this.node.active = visible;
    }

    /**
     * Foundation 继承自 LifeCycle
     */
    protected onDisable(): void {
        Dispatcher.entity.targetOff(this);
        super.onDisable();
    }

    /**
     * 添加事件
     * @param type
     * @param callback
     */
    public addEvent(type: string, callback: (...args: any[]) => void) {
        Dispatcher.entity.on(type, callback, this);
    }

    /**
     * 延迟调用
     * @param cb
     * @param delay
     */
    public delayCall(cb: () => void, delay: number) {
        this.scheduleOnce(cb, delay);
    }

    /**
     * 添加时钟
     * @param cb 回调函数，返回是否调度完成(true：完成，false:未完成)
     * @param interval 时间间隔(s)
     */
    public addClock(cb: ClockFn, interval: number = 1) {
        if (cb.call(this)) return;

        for (let clock of this._clocks) {
            if (clock.origin == cb) {
                Log.w('cb has already reg');
                return;
            }
        }

        let agent = () => {
            if (cb.call(this)) this.removeClock(cb);
        };
        this._clocks.push({ origin: cb, agent });
        this.schedule(agent, interval);
    }

    /**
     * 删除时钟
     * @param cb 回调函数
     */
    public removeClock(cb: ClockFn) {
        let index = this._clocks.findIndex(clock => {
            return clock.origin === cb;
        });
        if (index !== -1) {
            let clock = this._clocks[index];
            this.unschedule(clock.agent);
            this._clocks.splice(index, 1);
        }
    }

    /**
     * 删除所有时钟
     */
    public removeAllClock() {
        for (let clock of this._clocks) {
            this.unschedule(clock.agent);
        }
        this._clocks = [];
    }
    //private
    private _clocks: { origin: ClockFn, agent: () => void }[] = [];

    private _widgetDatas: IWidgetData[] = [];
    private insertWidget<T extends BaseWidget>(uuid: string, comp: T) {
        for (let widgetData of this._widgetDatas) {
            if (widgetData.uuid == uuid) {
                widgetData.comp = comp;
                break;
            }
        }
    }
}